package org.example;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import java.util.LinkedList;
import java.util.Queue;

public class Main extends Application {

    String iconSrc = "img/icon.png";

    String Title = "JDecibel";

    private volatile boolean stopped = false;
    private double maxDb = Double.MIN_VALUE;
    private double sumDb = 0.0;
    private int countDb = 0;
    private final Queue<Double> dbQueue = new LinkedList<>();
    private final int MAX_DATA_POINTS = 50;

    @Override
    public void start(Stage primaryStage) {
        VBox root = new VBox(10);
        Button startButton = new Button("开始");
        Button aboutButton = new Button("关于");
        Label dbLabel = new Label("实时分贝: ");
        Label maxDbLabel = new Label("最大分贝: ");
        Label averageDbLabel = new Label("平均分贝: ");
        Label levelDbLabel = new Label("影响: ");

        // 新增一个Line Chart
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("");
        final LineChart<String, Number> lineChart = new LineChart<>(xAxis, yAxis);
        lineChart.setTitle("");
        XYChart.Series series = new XYChart.Series();
        series.setName("实时分贝");
        lineChart.getData().add(series);

        startButton.setOnAction(event -> {
            stopped = false;
            maxDb = Double.MIN_VALUE;
            sumDb = 0.0;
            countDb = 0;
            dbQueue.clear();
            series.getData().clear();

            Task<Void> task = new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    AudioFormat format = new AudioFormat(16000, 16, 1, true, false);
                    DataLine.Info targetInfo = new DataLine.Info(TargetDataLine.class, format);
                    TargetDataLine targetLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
                    targetLine.open(format);
                    targetLine.start();

                    int bufferSize = 1600;
                    byte[] buffer = new byte[bufferSize];

                    while (!stopped) {
                        int count = targetLine.read(buffer, 0, buffer.length);
                        if (count > 0) {
                            double rms = calculateRMSLevel(buffer);
                            double db = 20 * Math.log10(rms);
                            Platform.runLater(() -> {
                                dbLabel.setText(String.format("实时分贝: " + "%.2f dB", db));
                                sumDb += db;
                                countDb++;
                                double averageDb = sumDb / countDb;
                                averageDbLabel.setText("平均分贝: " + String.format("%.2f dB", averageDb));

                                if (db > 0) {
                                    levelDbLabel.setText("影响: " + DecibelChecker.checkDecibelLevel(db));
                                }
                                if (db > maxDb) {
                                    maxDb = db;
                                    maxDbLabel.setText("最大分贝: " + String.format("%.2f dB", maxDb));
                                }

                                // 在队列中添加当前的分贝值，并且维护队列的长度
                                dbQueue.offer(db);
                                while (dbQueue.size() > MAX_DATA_POINTS) {
                                    dbQueue.poll();
                                }
                                // 更新Line Chart中的数据
                                series.getData().clear();
                                int i = 0;
                                for (Double dbValue : dbQueue) {
                                    series.getData().add(new XYChart.Data<>(String.valueOf(i), dbValue));
                                    i++;
                                }
                            });
                        }
                    }

                    targetLine.stop();
                    targetLine.close();
                    return null;
                }
            };
            new Thread(task).start();
        });

        Button stopButton = new Button("停止");
        stopButton.setOnAction(event -> stopped = true);

        aboutButton.setOnAction(event -> {
            // 创建一个新的舞台
            Stage aboutStage = new Stage();
            aboutStage.setScene(new Scene(new AboutPage(), 400, 300));
            aboutStage.getIcons().add(new Image(iconSrc));
            aboutStage.setTitle(Title);
            aboutStage.show();
        });


        HBox toolsHbox = new HBox(10, startButton, stopButton, aboutButton);
        root.getChildren().addAll(toolsHbox, dbLabel, maxDbLabel, averageDbLabel, levelDbLabel, lineChart);

        root.setPadding(new Insets(10));
        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.getIcons().add(new Image(iconSrc));
        primaryStage.setTitle(Title);
        primaryStage.show();
        primaryStage.setOnCloseRequest(event -> stopped = true);
    }

    private static double calculateRMSLevel(byte[] audioData) {
        long lSum = 0;
        for (byte datum : audioData) {
            lSum += datum;
        }

        double dAvg = (double) lSum / audioData.length;

        double sumMeanSquare = 0d;
        for (byte audioDatum : audioData) {
            sumMeanSquare += Math.pow(audioDatum - dAvg, 2d);
        }

        double averageMeanSquare = sumMeanSquare / audioData.length;
        return Math.sqrt(averageMeanSquare);
    }


    public static void main(String[] args) {
        launch(args);
    }
}